home *** CD-ROM | disk | FTP | other *** search
/ Freelog 125 / Freelog_MarsAvril2015_No125.iso / ViePratique / ArchiFacile / ArchiFacileSetup.exe / {app} / nw.pak / Unnamed File 000113.txt < prev    next >
Text File  |  2014-10-14  |  14KB  |  446 lines

  1. // Copyright (c) 2012 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4.  
  5. /**
  6.  * @fileoverview This is a data model representin
  7.  */
  8.  
  9. cr.define('cr.ui', function() {
  10.   /** @const */ var EventTarget = cr.EventTarget;
  11.  
  12.   /**
  13.    * A data model that wraps a simple array and supports sorting by storing
  14.    * initial indexes of elements for each position in sorted array.
  15.    * @param {!Array} array The underlying array.
  16.    * @constructor
  17.    * @extends {EventTarget}
  18.    */
  19.   function ArrayDataModel(array) {
  20.     this.array_ = array;
  21.     this.indexes_ = [];
  22.     this.compareFunctions_ = {};
  23.  
  24.     for (var i = 0; i < array.length; i++) {
  25.       this.indexes_.push(i);
  26.     }
  27.   }
  28.  
  29.   ArrayDataModel.prototype = {
  30.     __proto__: EventTarget.prototype,
  31.  
  32.     /**
  33.      * The length of the data model.
  34.      * @type {number}
  35.      */
  36.     get length() {
  37.       return this.array_.length;
  38.     },
  39.  
  40.     /**
  41.      * Returns the item at the given index.
  42.      * This implementation returns the item at the given index in the sorted
  43.      * array.
  44.      * @param {number} index The index of the element to get.
  45.      * @return {*} The element at the given index.
  46.      */
  47.     item: function(index) {
  48.       if (index >= 0 && index < this.length)
  49.         return this.array_[this.indexes_[index]];
  50.       return undefined;
  51.     },
  52.  
  53.     /**
  54.      * Returns compare function set for given field.
  55.      * @param {string} field The field to get compare function for.
  56.      * @return {function(*, *): number} Compare function set for given field.
  57.      */
  58.     compareFunction: function(field) {
  59.       return this.compareFunctions_[field];
  60.     },
  61.  
  62.     /**
  63.      * Sets compare function for given field.
  64.      * @param {string} field The field to set compare function.
  65.      * @param {function(*, *): number} Compare function to set for given field.
  66.      */
  67.     setCompareFunction: function(field, compareFunction) {
  68.       if (!this.compareFunctions_) {
  69.         this.compareFunctions_ = {};
  70.       }
  71.       this.compareFunctions_[field] = compareFunction;
  72.     },
  73.  
  74.     /**
  75.      * Returns true if the field has a compare function.
  76.      * @param {string} field The field to check.
  77.      * @return {boolean} True if the field is sortable.
  78.      */
  79.     isSortable: function(field) {
  80.       return this.compareFunctions_ && field in this.compareFunctions_;
  81.     },
  82.  
  83.     /**
  84.      * Returns current sort status.
  85.      * @return {!Object} Current sort status.
  86.      */
  87.     get sortStatus() {
  88.       if (this.sortStatus_) {
  89.         return this.createSortStatus(
  90.             this.sortStatus_.field, this.sortStatus_.direction);
  91.       } else {
  92.         return this.createSortStatus(null, null);
  93.       }
  94.     },
  95.  
  96.     /**
  97.      * Returns the first matching item.
  98.      * @param {*} item The item to find.
  99.      * @param {number=} opt_fromIndex If provided, then the searching start at
  100.      *     the {@code opt_fromIndex}.
  101.      * @return {number} The index of the first found element or -1 if not found.
  102.      */
  103.     indexOf: function(item, opt_fromIndex) {
  104.       for (var i = opt_fromIndex || 0; i < this.indexes_.length; i++) {
  105.         if (item === this.item(i))
  106.           return i;
  107.       }
  108.       return -1;
  109.     },
  110.  
  111.     /**
  112.      * Returns an array of elements in a selected range.
  113.      * @param {number=} opt_from The starting index of the selected range.
  114.      * @param {number=} opt_to The ending index of selected range.
  115.      * @return {Array} An array of elements in the selected range.
  116.      */
  117.     slice: function(opt_from, opt_to) {
  118.       var arr = this.array_;
  119.       return this.indexes_.slice(opt_from, opt_to).map(
  120.           function(index) { return arr[index] });
  121.     },
  122.  
  123.     /**
  124.      * This removes and adds items to the model.
  125.      * This dispatches a splice event.
  126.      * This implementation runs sort after splice and creates permutation for
  127.      * the whole change.
  128.      * @param {number} index The index of the item to update.
  129.      * @param {number} deleteCount The number of items to remove.
  130.      * @param {...*} The items to add.
  131.      * @return {!Array} An array with the removed items.
  132.      */
  133.     splice: function(index, deleteCount, var_args) {
  134.       var addCount = arguments.length - 2;
  135.       var newIndexes = [];
  136.       var deletePermutation = [];
  137.       var deletedItems = [];
  138.       var newArray = [];
  139.       index = Math.min(index, this.indexes_.length);
  140.       deleteCount = Math.min(deleteCount, this.indexes_.length - index);
  141.       // Copy items before the insertion point.
  142.       for (var i = 0; i < index; i++) {
  143.         newIndexes.push(newArray.length);
  144.         deletePermutation.push(i);
  145.         newArray.push(this.array_[this.indexes_[i]]);
  146.       }
  147.       // Delete items.
  148.       for (; i < index + deleteCount; i++) {
  149.         deletePermutation.push(-1);
  150.         deletedItems.push(this.array_[this.indexes_[i]]);
  151.       }
  152.       // Insert new items instead deleted ones.
  153.       for (var j = 0; j < addCount; j++) {
  154.         newIndexes.push(newArray.length);
  155.         newArray.push(arguments[j + 2]);
  156.       }
  157.       // Copy items after the insertion point.
  158.       for (; i < this.indexes_.length; i++) {
  159.         newIndexes.push(newArray.length);
  160.         deletePermutation.push(i - deleteCount + addCount);
  161.         newArray.push(this.array_[this.indexes_[i]]);
  162.       }
  163.  
  164.       this.indexes_ = newIndexes;
  165.  
  166.       this.array_ = newArray;
  167.  
  168.       // TODO(arv): Maybe unify splice and change events?
  169.       var spliceEvent = new Event('splice');
  170.       spliceEvent.removed = deletedItems;
  171.       spliceEvent.added = Array.prototype.slice.call(arguments, 2);
  172.  
  173.       var status = this.sortStatus;
  174.       // if sortStatus.field is null, this restores original order.
  175.       var sortPermutation = this.doSort_(this.sortStatus.field,
  176.                                          this.sortStatus.direction);
  177.       if (sortPermutation) {
  178.         var splicePermutation = deletePermutation.map(function(element) {
  179.           return element != -1 ? sortPermutation[element] : -1;
  180.         });
  181.         this.dispatchPermutedEvent_(splicePermutation);
  182.         spliceEvent.index = sortPermutation[index];
  183.       } else {
  184.         this.dispatchPermutedEvent_(deletePermutation);
  185.         spliceEvent.index = index;
  186.       }
  187.  
  188.       this.dispatchEvent(spliceEvent);
  189.  
  190.       // If real sorting is needed, we should first call prepareSort (data may
  191.       // change), and then sort again.
  192.       // Still need to finish the sorting above (including events), so
  193.       // list will not go to inconsistent state.
  194.       if (status.field)
  195.         this.delayedSort_(status.field, status.direction);
  196.  
  197.       return deletedItems;
  198.     },
  199.  
  200.     /**
  201.      * Appends items to the end of the model.
  202.      *
  203.      * This dispatches a splice event.
  204.      *
  205.      * @param {...*} The items to append.
  206.      * @return {number} The new length of the model.
  207.      */
  208.     push: function(var_args) {
  209.       var args = Array.prototype.slice.call(arguments);
  210.       args.unshift(this.length, 0);
  211.       this.splice.apply(this, args);
  212.       return this.length;
  213.     },
  214.  
  215.     /**
  216.      * Updates the existing item with the new item.
  217.      *
  218.      * The existing item and the new item are regarded as the same item and the
  219.      * permutation tracks these indexes.
  220.      *
  221.      * @param {*} oldItem Old item that is contained in the model. If the item
  222.      *     is not found in the model, the method call is just ignored.
  223.      * @param {*} newItem New item.
  224.      */
  225.     replaceItem: function(oldItem, newItem) {
  226.       var index = this.indexOf(oldItem);
  227.       if (index < 0)
  228.         return;
  229.       this.array_[this.indexes_[index]] = newItem;
  230.       this.updateIndex(index);
  231.     },
  232.  
  233.     /**
  234.      * Use this to update a given item in the array. This does not remove and
  235.      * reinsert a new item.
  236.      * This dispatches a change event.
  237.      * This runs sort after updating.
  238.      * @param {number} index The index of the item to update.
  239.      */
  240.     updateIndex: function(index) {
  241.       this.updateIndexes([index]);
  242.     },
  243.  
  244.     /**
  245.      * Notifies of update of the items in the array. This does not remove and
  246.      * reinsert new items.
  247.      * This dispatches one or more change events.
  248.      * This runs sort after updating.
  249.      * @param {Array.<number>} indexes The index list of items to update.
  250.      */
  251.     updateIndexes: function(indexes) {
  252.       var isIndexesValid = indexes.every(function(index) {
  253.         return 0 <= index && index < this.length;
  254.       }, this);
  255.       if (!isIndexesValid)
  256.         throw Error('Invalid index, ' + indexes[i]);
  257.  
  258.       for (var i = 0; i < indexes.length; i++) {
  259.         var e = new Event('change');
  260.         e.index = indexes[i];
  261.         this.dispatchEvent(e);
  262.       }
  263.  
  264.       if (this.sortStatus.field) {
  265.         var status = this.sortStatus;
  266.         var sortPermutation = this.doSort_(this.sortStatus.field,
  267.                                            this.sortStatus.direction);
  268.         if (sortPermutation)
  269.           this.dispatchPermutedEvent_(sortPermutation);
  270.         // We should first call prepareSort (data may change), and then sort.
  271.         // Still need to finish the sorting above (including events), so
  272.         // list will not go to inconsistent state.
  273.         this.delayedSort_(status.field, status.direction);
  274.       }
  275.     },
  276.  
  277.     /**
  278.      * Creates sort status with given field and direction.
  279.      * @param {string} field Sort field.
  280.      * @param {string} direction Sort direction.
  281.      * @return {!Object} Created sort status.
  282.      */
  283.     createSortStatus: function(field, direction) {
  284.       return {
  285.         field: field,
  286.         direction: direction
  287.       };
  288.     },
  289.  
  290.     /**
  291.      * Called before a sort happens so that you may fetch additional data
  292.      * required for the sort.
  293.      *
  294.      * @param {string} field Sort field.
  295.      * @param {function()} callback The function to invoke when preparation
  296.      *     is complete.
  297.      */
  298.     prepareSort: function(field, callback) {
  299.       callback();
  300.     },
  301.  
  302.     /**
  303.      * Sorts data model according to given field and direction and dispathes
  304.      * sorted event with delay. If no need to delay, use sort() instead.
  305.      * @param {string} field Sort field.
  306.      * @param {string} direction Sort direction.
  307.      * @private
  308.      */
  309.     delayedSort_: function(field, direction) {
  310.       var self = this;
  311.       setTimeout(function() {
  312.         // If the sort status has been changed, sorting has already done
  313.         // on the change event.
  314.         if (field == self.sortStatus.field &&
  315.             direction == self.sortStatus.direction) {
  316.           self.sort(field, direction);
  317.         }
  318.       }, 0);
  319.     },
  320.  
  321.     /**
  322.      * Sorts data model according to given field and direction and dispathes
  323.      * sorted event.
  324.      * @param {string} field Sort field.
  325.      * @param {string} direction Sort direction.
  326.      */
  327.     sort: function(field, direction) {
  328.       var self = this;
  329.  
  330.       this.prepareSort(field, function() {
  331.         var sortPermutation = self.doSort_(field, direction);
  332.         if (sortPermutation)
  333.           self.dispatchPermutedEvent_(sortPermutation);
  334.         self.dispatchSortEvent_();
  335.       });
  336.     },
  337.  
  338.     /**
  339.      * Sorts data model according to given field and direction.
  340.      * @param {string} field Sort field.
  341.      * @param {string} direction Sort direction.
  342.      * @private
  343.      */
  344.     doSort_: function(field, direction) {
  345.       var compareFunction = this.sortFunction_(field, direction);
  346.       var positions = [];
  347.       for (var i = 0; i < this.length; i++) {
  348.         positions[this.indexes_[i]] = i;
  349.       }
  350.       var sorted = this.indexes_.every(function(element, index, array) {
  351.         return index == 0 || compareFunction(element, array[index - 1]) >= 0;
  352.       });
  353.       if (!sorted)
  354.         this.indexes_.sort(compareFunction);
  355.       this.sortStatus_ = this.createSortStatus(field, direction);
  356.       var sortPermutation = [];
  357.       var changed = false;
  358.       for (var i = 0; i < this.length; i++) {
  359.         if (positions[this.indexes_[i]] != i)
  360.           changed = true;
  361.         sortPermutation[positions[this.indexes_[i]]] = i;
  362.       }
  363.       if (changed)
  364.         return sortPermutation;
  365.       return null;
  366.     },
  367.  
  368.     dispatchSortEvent_: function() {
  369.       var e = new Event('sorted');
  370.       this.dispatchEvent(e);
  371.     },
  372.  
  373.     dispatchPermutedEvent_: function(permutation) {
  374.       var e = new Event('permuted');
  375.       e.permutation = permutation;
  376.       e.newLength = this.length;
  377.       this.dispatchEvent(e);
  378.     },
  379.  
  380.     /**
  381.      * Creates compare function for the field.
  382.      * Returns the function set as sortFunction for given field
  383.      * or default compare function
  384.      * @param {string} field Sort field.
  385.      * @param {function(*, *): number} Compare function.
  386.      * @private
  387.      */
  388.     createCompareFunction_: function(field) {
  389.       var compareFunction =
  390.           this.compareFunctions_ ? this.compareFunctions_[field] : null;
  391.       var defaultValuesCompareFunction = this.defaultValuesCompareFunction;
  392.       if (compareFunction) {
  393.         return compareFunction;
  394.       } else {
  395.         return function(a, b) {
  396.           return defaultValuesCompareFunction.call(null, a[field], b[field]);
  397.         }
  398.       }
  399.       return compareFunction;
  400.     },
  401.  
  402.     /**
  403.      * Creates compare function for given field and direction.
  404.      * @param {string} field Sort field.
  405.      * @param {string} direction Sort direction.
  406.      * @param {function(*, *): number} Compare function.
  407.      * @private
  408.      */
  409.     sortFunction_: function(field, direction) {
  410.       var compareFunction = null;
  411.       if (field !== null)
  412.         compareFunction = this.createCompareFunction_(field);
  413.       var dirMultiplier = direction == 'desc' ? -1 : 1;
  414.  
  415.       return function(index1, index2) {
  416.         var item1 = this.array_[index1];
  417.         var item2 = this.array_[index2];
  418.  
  419.         var compareResult = 0;
  420.         if (typeof(compareFunction) === 'function')
  421.           compareResult = compareFunction.call(null, item1, item2);
  422.         if (compareResult != 0)
  423.           return dirMultiplier * compareResult;
  424.         return dirMultiplier * this.defaultValuesCompareFunction(index1,
  425.                                                                  index2);
  426.       }.bind(this);
  427.     },
  428.  
  429.     /**
  430.      * Default compare function.
  431.      */
  432.     defaultValuesCompareFunction: function(a, b) {
  433.       // We could insert i18n comparisons here.
  434.       if (a < b)
  435.         return -1;
  436.       if (a > b)
  437.         return 1;
  438.       return 0;
  439.     }
  440.   };
  441.  
  442.   return {
  443.     ArrayDataModel: ArrayDataModel
  444.   };
  445. });
  446.